package com.whz.generic.utils;

import com.whz.generic.ByteUtils;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author whz
 */
@Slf4j
@UtilityClass
public class IpUtils {

    private static final String[] HEADERS = {
            "x-forwarded-for",
            "Proxy-Client-IP",
            "WL-Proxy-Client-IP",
            "HTTP_CLIENT_IP",
            "X-Real-IP",
    };
    public static final String UNKNOWN = "unknown";


    /**
     * 私有IP：
     * A类   10.0.0.0-10.255.255.255
     * B类   172.16.0.0-172.31.255.255
     * C类   192.168.0.0-192.168.255.255
     * <p>
     * 127这个网段是环回地址
     * localhost
     */
    private static final List<Pattern> IP_FILTER_REGEX_LIST = new ArrayList<>();

    static {
        Set<String> ipFilter = new HashSet<String>();
        ipFilter.add("^10\\.(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[0-9])"
                     + "\\.(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[0-9])" +
                     "\\.(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[0-9])$");
        // B类地址范围: 172.16.0.0---172.31.255.255
        ipFilter.add("^172\\.(1[6789]|2[0-9]|3[01])\\" + ".(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[0-9])\\"
                     + ".(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[0-9])$");
        // C类地址范围: 192.168.0.0---192.168.255.255
        ipFilter.add("^192\\.168\\.(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[0-9])\\"
                     + ".(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[0-9])$");
        ipFilter.add("127.0.0.1");
        ipFilter.add("0.0.0.0");
        ipFilter.add("localhost");
        for (String tmp : ipFilter) {
            IP_FILTER_REGEX_LIST.add(Pattern.compile(tmp));
        }
    }

    /**
     * 获取本地IP
     *
     * @return 本地IP 或 null
     */
    public static InetAddress getLocalAddress() {
        try {
            InetAddress candidateAddress = null;
            Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
            while (networkInterfaces.hasMoreElements()) {
                NetworkInterface iface = networkInterfaces.nextElement();
                // 该网卡接口下的ip会有多个，也需要一个个的遍历，找到自己所需要的
                for (Enumeration<InetAddress> inetAddress = iface.getInetAddresses();
                     inetAddress.hasMoreElements(); ) {
                    InetAddress inetAdder = inetAddress.nextElement();
                    // 排除回环类型地址（不管是IPv4还是IPv6 只要是回环地址都会返回true）
                    if (!inetAdder.isLoopbackAddress()) {
                        if (inetAdder.isSiteLocalAddress()) {
                            // 如果是site-local地址，就是它了 就是我们要找的
                            return inetAdder;
                        }
                        // 若不是site-local地址 那就记录下该地址当作候选
                        if (candidateAddress == null) {
                            candidateAddress = inetAdder;
                        }
                    }
                }
            }
            // 如果除回环地之外无其它地址了，那就回退到原始方案吧
            return candidateAddress == null? InetAddress.getLocalHost() : candidateAddress;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取远程用户IP
     *
     * @param request 前端请求
     * @return 远程IP 或 unknown
     */
    public static String getRemoteIpAddrByRequest(final HttpServletRequest request) {
        String ip = "";
        for (String header : HEADERS) {
            ip = request.getHeader(header);
            if (check(ip)) {
                continue;
            }
            log.info("Get remote ip:{} from request-header:{}", ip, header);
            if (ip.contains(",")) {
                ip = ip.split(",")[0];
            }
            if (!check(ip)) {
                return StringUtils.trimWhitespace(ip);
            }
        }
        if (check(ip)) {
            ip = request.getRemoteAddr();
        }
        return StringUtils.trimWhitespace(ip);
    }

    private static boolean check(String ip) {
        return StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(StringUtils.trimWhitespace(ip));
    }

    /**
     * 从上下文的request获取远程IP
     *
     * @return 远程IP
     */
    public static String getRemoteIp() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) Objects.requireNonNull(
                RequestContextHolder.getRequestAttributes(),
                "无法获取request");
        return getRemoteIpAddrByRequest(attributes.getRequest());
    }

    /**
     * 本地IP的int数字
     *
     * @return IP的int数字
     */
    public static long getLocalIpNum() {
        return ByteUtils.getLongQuickly(getLocalAddress().getAddress());
    }


    /**
     * 判断IP是否内网IP
     *
     * @param ip ip address
     * @return true 是内网ip
     */
    public static boolean isInnerIp(final String ip) {
        boolean isInnerIp = false;
        for (Pattern tmp : IP_FILTER_REGEX_LIST) {
            Matcher matcher = tmp.matcher(ip);
            if (matcher.find()) {
                isInnerIp = true;
                break;
            }
        }
        return isInnerIp;
    }
}